/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package merlion.scm.lcm.session;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import merlion.common.util.Consts;
import merlion.crm.sopm.entity.ItemLine;
import merlion.mrp.pmm.entity.FinishedGood;
import merlion.scm.icm.entity.InventoryReservation;
import merlion.scm.icm.entity.ProductInventoryRecord;
import merlion.scm.lcm.entity.DeliveryOrder;
import merlion.scm.lcm.entity.LogisticItemLine;
import merlion.scm.lcm.entity.PackingOrder;
import merlion.scm.lcm.entity.PickingOrder;
import merlion.scm.lcm.entity.ShippingOrder;

/**
 *
 * @author Yuwei
 */
@Stateless
public class LogisticControlSessionBean implements LogisticControlSessionBeanLocal {

    @PersistenceContext
    private EntityManager entityManager;

//    @Override
//    public void rankDeliveryOrder(DeliveryOrder deliveryOrder) {
//        //set priority for deliveryOrder
//        // retrieve DeliveryOrders and append new added DeliveryOrder
//        Query query = entityManager.createQuery("SELECT d FROM DeliveryOrder d");
//        List<DeliveryOrder> dos = query.getResultList();
//        dos.add(deliveryOrder);
//
//        // create a new list to store sorted DeliveryOrders
//        List<DeliveryOrder> sortedDos = new LinkedList<DeliveryOrder>();
//        // bubble sort
//        for (int i = 0; i < dos.size(); i++) {
//            int insertIdx = 0;
//            for (; insertIdx < sortedDos.size(); insertIdx++) {
//                if (sortedDos.get(insertIdx).getLeadTimeTS().after(dos.get(i).getLeadTimeTS())) {
//                    break;
//                }
//            }
//            sortedDos.add(insertIdx, dos.get(i));
//        }
//
//        // reset priority according to sorted sequence
//        for (int i = 0; i < sortedDos.size(); i++) {
//            sortedDos.get(i).setPriority(i + 1);
//        }
//        for (int i = 0; i < sortedDos.size(); i++) {
//            entityManager.merge(sortedDos.get(i));
//        }
//    }
    @Override
    public String generateDeliveryOrderId() {

        //Get date YYYYMMDD
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
        String frontId = sdf.format(cal.getTime());

        //Query Saleslead table, anything like 'yyMMdd'
        Query query = entityManager.createQuery("SELECT d FROM DeliveryOrder d WHERE d.deliveryOrderIdStr LIKE :p1");
        query.setParameter("p1", "%" + frontId + "%");

        //Get result size
        int size = query.getResultList().size() + 1;
        String backId = String.format("%04d", size);

        //Set new ID to 'YYYYMMDD' + 'size + 1'
        String id = "DO" + frontId + "/" + backId;
        //return id
        return id;
    }
    
    @Override
    public List<DeliveryOrder> getAllDeliveryOrder(){
        Query query = entityManager.createQuery("SELECT d FROM DeliveryOrder d");
        return query.getResultList();
    }

    @Override
    public void generateLogisticControlOrders(DeliveryOrder deliveryOrder) {

        //generate picking order
        System.out.println("Picking order starting: flush!");
        entityManager.flush();
        PickingOrder pickingOrder = generatePickingOrder(deliveryOrder);
        //generate packing order
        System.out.println("Packing order starting: flush!");
        entityManager.flush();
        PackingOrder packingOrder = generatePackingOrder(deliveryOrder);
        //generate shipping order
        System.out.println("Shipping order starting: flush!");
        entityManager.flush();
        ShippingOrder shippingOrder = generateShippingOrder(packingOrder);

        deliveryOrder.setStatus(Consts.LOGISTIC_DELIVERYORDER_PICKING);
        System.out.println("Delivery order set status.");
        //entityManager.merge(deliveryOrder);
        System.out.println("merge complete");
    }

    private PickingOrder generatePickingOrder(DeliveryOrder deliveryOrder) {
        System.out.println("Generate Picking Order");
        PickingOrder pickingOrder = new PickingOrder();
        deliveryOrder = entityManager.find(DeliveryOrder.class, deliveryOrder.getId());
        pickingOrder.setDeliveryOrder(deliveryOrder);
        deliveryOrder.setPickingOrder(pickingOrder);
        deliveryOrder.setStatus(Consts.LOGISTIC_DELIVERYORDER_PICKING);
        pickingOrder.setLogisticItemLines(new ArrayList<LogisticItemLine>());
        pickingOrder.setPickingOrderId(generatePickingOrderId());

        System.out.println("Picking order set delivery Order and log il: flush!");
        entityManager.flush();

        ArrayList<LogisticItemLine> pickingIls = new ArrayList<LogisticItemLine>();
        ArrayList<ItemLine> dList = new ArrayList<ItemLine>(deliveryOrder.getItemLines());

        for (int i = 0; i < dList.size(); i++) {
            ItemLine il = dList.get(i);
            if(il.getQtyOrderedCase()==0)
                continue;
            ArrayList<InventoryReservation> reservations = new ArrayList<InventoryReservation>(il.getReservations());
            System.out.println("Generate Picking Order: itemline reservations size: " + reservations.size());
            for (int j = 0; j < reservations.size(); j++) {
                System.out.println("Generate Picking Order: j = " + j);
                FinishedGood batch = reservations.get(j).getBatch();
                double toPickQty = reservations.get(j).getReservationQty();
                for (int k = 0; k < batch.getLocations().size(); k++) {
                    System.out.println("Generate Picking Order: k = " + k);
                    LogisticItemLine pickingIl = new LogisticItemLine();
                    System.out.println("Generate Picking Order: One picking logistic itemline generated.");
                    pickingIl.setProduct(batch.getInventory().getProduct());
                    pickingIl.setBatchNo(batch.getBatchNo());
                    double binAvailableQty = batch.getLocations().get(k).getStoredQty();
                    System.out.println("Generate Picking Order: binAvailableQty: " + binAvailableQty);
                    System.out.println("Generate Picking Order: toPickQty: " + toPickQty);
                    if(binAvailableQty == 0)
                        continue;
                    if (binAvailableQty >= toPickQty) {
                        System.out.println("Generate Picking Order: binAvailableQty >= toPickQty");
                        pickingIl.setWarehouseLoc(batch.getLocations().get(k));
                        pickingIl.setPickingQty(toPickQty);
                        pickingIls.add(pickingIl);
                        batch.getLocations().get(k).setStoredQty(binAvailableQty - toPickQty);
                        break;
                    } else {
                        System.out.println("Generate Picking Order: binAvailableQty < toPickQty");
                        pickingIl.setWarehouseLoc(batch.getLocations().get(k));
                        pickingIl.setPickingQty(binAvailableQty);
                        toPickQty = toPickQty - binAvailableQty;
                        pickingIls.add(pickingIl);
                        batch.getLocations().get(k).setStoredQty(0D);
                    }
                    if(toPickQty == 0){
                        break;
                    }
                }
            }
            ProductInventoryRecord pir = il.getProduct().getPir();
            pir.setBalance(pir.getBalance() - il.getQtyOrderedCase());
            pir.setReservedQty(pir.getReservedQty() - il.getQtyOrderedCase());
        }
        pickingOrder.setLogisticItemLines(pickingIls);
        System.out.println("picking IL Size: " + pickingIls.size());
        for (LogisticItemLine i : pickingIls) {
            System.out.println("i to string: " + i.toString());
            System.out.println("batch" + i.getBatchNo());
            System.out.println("picking qty" + i.getPickingQty());
        }

        System.out.println("Picking order starting: b4 flush!");
        
        pickingOrder = entityManager.merge(pickingOrder);
        System.out.println("Picking order starting: after flush!");
        entityManager.flush();
        System.out.println("Picking order starting: after flush b4 return!");
        return pickingOrder;
    }

    private PackingOrder generatePackingOrder(DeliveryOrder deliveryOrder) {
        Query query = entityManager.createQuery("SELECT d FROM DeliveryOrder d WHERE d.id=:p1");
        query.setParameter("p1", deliveryOrder.getId());
        deliveryOrder = (DeliveryOrder) query.getSingleResult();
        System.out.println("Generate Packing Order");
        PackingOrder packingOrder = new PackingOrder();
        packingOrder.setDeliveryOrder(deliveryOrder);
        
        packingOrder.setPackingOrderId(generatePackingOrderId());
        List<LogisticItemLine> pickingIls = deliveryOrder.getPickingOrder().getLogisticItemLines();
        List<LogisticItemLine> packingIls = new ArrayList<LogisticItemLine>();
        double containerSize = 500;

        Integer containerNo = 1;
        LogisticItemLine pickingIl;
        LogisticItemLine packingIl;
        for (int i = 0; i < pickingIls.size(); i++) {
            pickingIl = pickingIls.get(i);
            double pickingIlQty = pickingIl.getPickingQty();
            while (pickingIlQty > 0) {
                if (pickingIlQty <= containerSize) {
                    packingIl = new LogisticItemLine();
                    packingIl.setPackingContainerId(containerNo);
                    packingIl.setProduct(pickingIl.getProduct());
                    packingIl.setWarehouseLoc(pickingIl.getWarehouseLoc());
                    packingIl.setPackingQty(pickingIl.getPickingQty());
                    packingIls.add(packingIl);
                    containerSize = containerSize - pickingIlQty;
                    pickingIlQty = 0;
                    if (containerSize == 0) {
                        containerNo++;
                        containerSize = 500;
                    }
                } else {
                    packingIl = new LogisticItemLine();
                    packingIl.setPackingContainerId(containerNo);
                    packingIl.setProduct(pickingIl.getProduct());
                    packingIl.setWarehouseLoc(pickingIl.getWarehouseLoc());
                    packingIl.setPackingQty(containerSize);
                    packingIls.add(packingIl);
                    pickingIlQty = pickingIlQty - containerSize;
                    containerNo++;
                    containerSize = 500;
                }
            }
        }
        packingOrder.setLogisticItemLines(packingIls);
        deliveryOrder.setPackingOrder(packingOrder);
//        System.out.println("Packing order starting: b4 flush!");
//        entityManager.flush();
        packingOrder = entityManager.merge(packingOrder);
        System.out.println("Packing order END: flush!");
        entityManager.flush();
        return packingOrder;
    }

    private ShippingOrder generateShippingOrder(PackingOrder packingOrder) {
        System.out.println("Generate Shipping Order");
        packingOrder = entityManager.find(PackingOrder.class, packingOrder.getId());
        ShippingOrder shippingOrder = new ShippingOrder();
        shippingOrder.setShippingOrderId(generateShippingOrderId());
        if(packingOrder.getDeliveryOrder().getSalesOrder().getPaymentMode() == true){
            //Cash In Advance
            shippingOrder.setInvoiced(Boolean.TRUE);
        }
        
        List<LogisticItemLine> shippingIls = new ArrayList<LogisticItemLine>();
        
        Query query = entityManager.createQuery("SELECT d FROM DeliveryOrder d WHERE d.id=:p1");
        query.setParameter("p1", packingOrder.getDeliveryOrder().getId());
        DeliveryOrder deliveryOrder = (DeliveryOrder) query.getSingleResult();
        
        LogisticItemLine shippingIl;
        
        List<ItemLine> deliveryOrderIls = (List<ItemLine>) deliveryOrder.getItemLines();
        
        for (int i = 0; i < deliveryOrderIls.size(); i++) {
            if(deliveryOrderIls.get(i).getQtyOrderedCase() == 0)
                continue;
            
            shippingIl = new LogisticItemLine();
            
            shippingIl.setProduct(deliveryOrderIls.get(i).getProduct());
            
            List<Integer> containerList = getContainerList(packingOrder, deliveryOrderIls.get(i).getProduct().getProductType());
            
            shippingIl.setContainerListStr(getcontainerListStr(containerList));
            
            shippingIl.setQtyOrdered(deliveryOrderIls.get(i).getQtyOrderedCase());
            
            shippingIls.add(shippingIl);
            
        }

        shippingOrder.setLogisticItemLines(shippingIls);
        //Calculate Total Weight
        Double weight = 0D;
        for(int i = 0; i < shippingIls.size(); i++){
            if(shippingIls.get(i).getProduct().getProductType().contains("Classic")){
                weight = weight + shippingIls.get(i).getQtyOrdered()*240*50/1000;
            }else{
                weight = weight + shippingIls.get(i).getQtyOrdered()*200*66.61/1000;
            }
        }
        shippingOrder.setWeight(weight);
        
        shippingOrder.setDeliveryOrder(deliveryOrder);
        deliveryOrder.setShippingOrder(shippingOrder);

        shippingOrder = entityManager.merge(shippingOrder);
        entityManager.flush();
        return shippingOrder;
    }

    private String getcontainerListStr(List<Integer> containerList){
        //TO DO: sort containerList
        
        System.out.println("Get Container List Str called.");
        String str = "";
        for(int i = 0; i < containerList.size(); i++){
            if(i > 0){
                str = str + ", " + containerList.get(i);
            }else{
                str = str + containerList.get(i);
            }           
        }
        System.out.println("Get Container List Str: Str = " + str);
        return str;
    }
    
    private List<Integer> getContainerList(PackingOrder packingOrder, String productType) {
        System.out.println("Get Container List called.");
        List<Integer> containerNos = new ArrayList<Integer>();
        for (int i = 0; i < packingOrder.getLogisticItemLines().size(); i++) {
            System.out.println("Get Container List: i = " + i);
            if (packingOrder.getLogisticItemLines().get(i).getProduct().getProductType().equals(productType)) {
                if (!containerNos.contains(packingOrder.getLogisticItemLines().get(i).getPackingContainerId())) {
                    containerNos.add(packingOrder.getLogisticItemLines().get(i).getPackingContainerId());
                }
            }
        }
        return containerNos;
    }

    private LogisticItemLine getTheLine(List<LogisticItemLine> ils, Integer containerNo, String productType) {
        LogisticItemLine line = null;
        for (int i = 0; i < ils.size(); i++) {
            if ((ils.get(i).getPackingContainerId() == containerNo) && (ils.get(i).getProduct().getProductType().equals(productType))) {
                line = ils.get(i);
            }
        }
        return line;
    }

    @Override
    public String generatePickingOrderId() {
        System.out.println("Generate Picking Order Id");
        //Get date YYYYMMDD
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
        String frontId = sdf.format(cal.getTime());

        //Query PickingOrder table, anything like 'yyMMdd'
        Query query = entityManager.createQuery("SELECT p FROM PickingOrder p WHERE p.pickingOrderId LIKE :p1");
        query.setParameter("p1", "%" + frontId + "%");

        //Get result size
        int size = query.getResultList().size() + 1;
        String backId = String.format("%04d", size);

        //Set new ID to 'YYYYMMDD' + 'size + 1'
        String id = "PIO" + frontId + "/" + backId;
        //return id
        return id;
    }

    @Override
    public String generatePackingOrderId() {
        System.out.println("Generate Packing Order Id ");
        //Get date YYYYMMDD
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
        String frontId = sdf.format(cal.getTime());

        //Query PickingOrder table, anything like 'yyMMdd'
        entityManager.clear();
        Query query = entityManager.createQuery("SELECT p FROM PackingOrder p WHERE p.packingOrderId LIKE :p1");
        query.setParameter("p1", "%" + frontId + "%");

        //Get result size
        int size = query.getResultList().size() + 1;
        String backId = String.format("%04d", size);

        //Set new ID to 'YYYYMMDD' + 'size + 1'
        String id = "PAO" + frontId + "/" + backId;
        //return id
        return id;
    }

    @Override
    public String generateShippingOrderId() {
        System.out.println("Generate Shipping Order Id");
        entityManager.clear();
        entityManager.flush();
        System.out.println("Flushed");
        //Get date YYYYMMDD
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
        String frontId = sdf.format(cal.getTime());
        System.out.println("query");
        //Query PickingOrder table, anything like 'yyMMdd'
        Query query = entityManager.createQuery("SELECT p FROM ShippingOrder p WHERE p.shippingOrderId LIKE :p1");
        query.setParameter("p1", "%" + frontId + "%");
        System.out.println("queried");
        //Get result size
        int size = query.getResultList().size() + 1;
        String backId = String.format("%04d", size);

        //Set new ID to 'YYYYMMDD' + 'size + 1'
        String id = "SPO" + frontId + "/" + backId;
        //return id
        return id;
    }

    @Override
    public List<DeliveryOrder> getConfirmedOrderList() {
        List<DeliveryOrder> dos = entityManager.createQuery("SELECT o FROM DeliveryOrder o WHERE o.status LIKE 'Queuing' AND o.salesOrder.salesOrderStatus LIKE 'Order Fulfilling'").getResultList();
        List<DeliveryOrder> todayDos = new ArrayList<DeliveryOrder>();
        Timestamp todayDate = new Timestamp(new Date().getTime());
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        for(int i = 0; i < dos.size(); i++){
            if(sdf.format(dos.get(i).getLeadTimeTS()).equals(sdf.format(todayDate))){
                todayDos.add(dos.get(i));
            }
        }
        return todayDos;
    }

    @Override
    public List<DeliveryOrder> getProcessingOrderList() {
        return entityManager.createQuery("SELECT o FROM DeliveryOrder o WHERE o.status NOT LIKE 'Queuing' AND o.status NOT LIKE 'Shipped' AND o.salesOrder.salesOrderStatus LIKE 'Order Fulfilling'").getResultList();
    }

    @Override
    public List<DeliveryOrder> getProcessedOrderList() {
        return entityManager.createQuery("SELECT o FROM DeliveryOrder o WHERE o.status LIKE 'Shipped'").getResultList();
    }

    @Override
    public DeliveryOrder getDeliveryOrderById(Long id) {
        return (DeliveryOrder) entityManager.createQuery("SELECT d FROM DeliveryOrder d WHERE d.id = " + id).getSingleResult();
    }

    @Override
    public PickingOrder setPickingOrderDone(PickingOrder p) {
        p.setStatus(true);
        p.getDeliveryOrder().setStatus(Consts.LOGISTIC_DELIVERYORDER_PACKING);
        return entityManager.merge(p);
    }

    @Override
    public PackingOrder setPackingOrderDone(PackingOrder p) {
        p.setDone(Boolean.TRUE);
        p.getDeliveryOrder().setStatus(Consts.LOGISTIC_DELIVERYORDER_SHIPPING);
        return entityManager.merge(p);
    }

    @Override
    public ShippingOrder setShippingOrderDone(ShippingOrder p) {
        p.setDone(Boolean.TRUE);
        p.getDeliveryOrder().setStatus(Consts.LOGISTIC_DELIVERYORDER_SHIPPED);
        boolean allShipped = true;
        if(p.getDeliveryOrder().getSalesOrder().getDeliveryOrders().size()==2){
            allShipped = false;
            //Check the other deliveryOrder
            List<DeliveryOrder> dos = (List<DeliveryOrder>) p.getDeliveryOrder().getSalesOrder().getDeliveryOrders();
            if(dos.get(0).getStatus().equals("Shipped") && dos.get(1).getStatus().equals("Shipped")){
                allShipped = true;
            }
        }
        if(allShipped == true){
            p.getDeliveryOrder().getSalesOrder().setSalesOrderStatus(Consts.ORDER_SHIPPED);
        }
        
        for(int i = 0; i < p.getLogisticItemLines().size(); i++){
            p.getLogisticItemLines().get(i).setQtyShipped(p.getLogisticItemLines().get(i).getQtyOrdered());
        }
        //Change CIA payment to real payment
        if(p.getDeliveryOrder().getInvoice().getCashInAdvance()==true){
            p.getDeliveryOrder().getInvoice().getCustomerPaymentRecord().setRealPayment(true);
        }            
        entityManager.merge(p);
        entityManager.flush();
        return entityManager.find(ShippingOrder.class, p.getId());
    }

    @Override
    public DeliveryOrder saveDeliveryOrder(DeliveryOrder d) {
        return entityManager.merge(d);
    }
}
